package node

import (
	"github.com/libp2p/go-libp2p-core/peer"
	"hundsun.com/hsl/hschain/p2p/impl/base"
)

//GetFilter 获取过滤器
func (n *Node) GetFilter(opts *base.MsgOptions) base.PeersFilter {
	fs := opts.Filters
	if len(opts.Filters) == 0 {
		return &ConnectedPeersFilter{node: n}
	}
	pfs := make([]base.PeersFilter, 0)
	for _, f := range fs {
		var filter base.PeersFilter
		switch f {
		case base.NearestStrategy:
			filter = &NearestPeersFilter{node: n}
		case base.ConnectedStrategy:
			filter = &ConnectedPeersFilter{node: n}
		default:
			filter = &ConnectedPeersFilter{node: n}
		}
		pfs = append(pfs, filter)
	}
	// process target peer IDs
	var peerIds []peer.ID
	if len(opts.TargetPeerIDs) > 0 {
		for _, tpid := range opts.TargetPeerIDs {
			peerid, err := peer.IDB58Decode(tpid)
			if err != nil {
				n.Log.Warn("getFilter parse peer ID failed", "pid", tpid, "error", err)
				continue
			}
			peerIds = append(peerIds, peerid)
		}
	}
	return NewMultiFilter(pfs, peerIds)
}

// NearestPeersFilter define filter that get nearest peers from a specified peer ID
type NearestPeersFilter struct {
	node *Node
}

// Filter 广播给最近的节点
func (nf *NearestPeersFilter) Filter() (interface{}, error) {
	// 查找最近10个节点
	peers := nf.node.ConnMgr.FetchNearestPeers(10)
	return peers, nil
}

// ConnectedPeersFilter define connected peers
type ConnectedPeersFilter struct {
	node *Node
}

// Filter 广播给连接的节点
func (cf *ConnectedPeersFilter) Filter() (interface{}, error) {
	peers := cf.node.ConnMgr.FetchConnPeers()
	return peers, nil
}

// MultiFilter a peer filter that contains multiple filters
type MultiFilter struct {
	filters  []base.PeersFilter
	extraPrs []peer.ID
}

// NewMultiFilter create instance of MultiStrategy
func NewMultiFilter(filters []base.PeersFilter, extraPeers []peer.ID) *MultiFilter {
	return &MultiFilter{
		filters:  filters,
		extraPrs: extraPeers,
	}
}

// Filter return peer IDs with multiple filters
func (mf *MultiFilter) Filter() (interface{}, error) {
	res := make([]peer.ID, 0)
	dupmp := make(map[string]struct{})
	// add all filters
	for _, filter := range mf.filters {
		peers, err := filter.Filter()
		if err != nil {
			return res, err
		}
		for _, peer := range peers.([]peer.ID) {
			if _, ok := dupmp[peer.Pretty()]; !ok {
				dupmp[peer.Pretty()] = struct{}{}
				res = append(res, peer)
			}
		}
	}
	// add extra peers
	for _, peer := range mf.extraPrs {
		if _, ok := dupmp[peer.Pretty()]; !ok {
			dupmp[peer.Pretty()] = struct{}{}
			res = append(res, peer)
		}
	}
	return res, nil
}
